package org.dicr.gui;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;

import org.dicr.event.*;
import org.dicr.gui.layout.*;

/***********************************************************************************************************************
 * @author Igor A Tarasov me@dicr.dn.ua
 * @version 030128
 **********************************************************************************************************************/
public class DateField extends JPanel implements ActionListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public static String[] monthList = new String[] { "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль",
	        "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" };

	protected Calendar calendar = Calendar.getInstance();

	protected JComboBox dayCombo = new JComboBox();

	protected JComboBox monthCombo = new JComboBox();

	protected JComboBox yearCombo = new JComboBox();

	protected EventListenerList listenerList = new EventListenerList();

	protected static String nullItem = "";

	/*******************************************************************************************************************
	 * Конструктор
	 ******************************************************************************************************************/
	public DateField() {
		try {
			jbInit();
		} catch (Exception ex) {
			DicrExceptionHandler.handleException(ex, this);
		}
	}

	/*******************************************************************************************************************
	 * Инициализация
	 ******************************************************************************************************************/
	private void jbInit() {
		this.setLayout(new PnutsLayout("cols=4, halign=fill, valign=fill, padx=0, pady=0, ipadx=0, ipady=0, expand=y"));
		// this.setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
		this.add(this.dayCombo);
		this.add(this.monthCombo);
		this.add(this.yearCombo);
		this.add(Box.createHorizontalGlue(), "expand=xy");

		this.calendar.setLenient(false);
		this.calendar.set(Calendar.HOUR_OF_DAY, 0);
		this.calendar.set(Calendar.MINUTE, 0);
		this.calendar.set(Calendar.SECOND, 0);
		this.calendar.set(Calendar.MILLISECOND, 0);
		int day = this.calendar.get(Calendar.DAY_OF_MONTH);
		int month = this.calendar.get(Calendar.MONTH);
		int year = this.calendar.get(Calendar.YEAR);
		this.dayCombo.addItem(nullItem);
		for (int i = 1; i <= 31; i++)
			this.dayCombo.addItem(Integer.valueOf(i));
		this.dayCombo.setSelectedItem(Integer.valueOf(day));
		this.dayCombo.setLightWeightPopupEnabled(true);
		this.dayCombo.setRequestFocusEnabled(false);
		this.dayCombo.setFont(this.getFont());
		this.dayCombo.addActionListener(this);
		this.monthCombo.addItem(nullItem);
		for (int i = 0; i < 12; i++)
			this.monthCombo.addItem(monthList[i]);
		this.monthCombo.setSelectedItem(monthList[month]);
		this.monthCombo.setLightWeightPopupEnabled(true);
		this.monthCombo.setRequestFocusEnabled(false);
		this.monthCombo.setFont(this.getFont());
		this.monthCombo.addActionListener(this);
		this.yearCombo.addItem(nullItem);
		for (int i = year - 20; i <= year + 20; i++)
			this.yearCombo.addItem(Integer.valueOf(i));
		this.yearCombo.setSelectedItem(Integer.valueOf(year));
		this.yearCombo.setLightWeightPopupEnabled(true);
		this.yearCombo.setRequestFocusEnabled(false);
		this.yearCombo.setFont(this.getFont());
		this.yearCombo.addActionListener(this);
	}

	/*******************************************************************************************************************
	 * Проверяет на завершенность всех полей даны в полях компонентов
	 ******************************************************************************************************************/
	public boolean isDateComplete() {
		if (this.dayCombo.getSelectedItem() != nullItem && this.monthCombo.getSelectedItem() != nullItem
		        && this.yearCombo.getSelectedItem() != nullItem) return true;
		return false;
	}

	/*******************************************************************************************************************
	 * Корректирует значения календаря на корректность пары день/месяц. Если дата установлена некорректно, то при чтении
	 * Calendar.get выбрасывается IllegalArgumentException. Возвращает true, если календарь был пофиксен, false, если
	 * дата была корректна.
	 ******************************************************************************************************************/
	protected boolean fixCalendar() {
		boolean fix = false;
		try {
			this.calendar.get(Calendar.DAY_OF_MONTH);
		} catch (Exception e) {
			this.calendar.set(Calendar.DAY_OF_MONTH, 1);
			this.calendar.set(Calendar.DAY_OF_MONTH, this.calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
			fix = true;
		}
		return fix;
	}

	/*******************************************************************************************************************
	 * Обновляет календарь при смене значений полей
	 ******************************************************************************************************************/
	protected void updateCalendar() {
		// если во всех полях выбраны числовые значения, устанавливаем календарь
		if (isDateComplete()) {
			int day = ((Integer) this.dayCombo.getSelectedItem()).intValue();
			int month = this.monthCombo.getSelectedIndex() - 1;
			int year = ((Integer) this.yearCombo.getSelectedItem()).intValue();
			this.calendar.set(Calendar.YEAR, year);
			this.calendar.set(Calendar.MONTH, month);
			this.calendar.set(Calendar.DAY_OF_MONTH, day);
			if (fixCalendar()) updateComponents();
		} else {
			this.calendar.set(Calendar.DAY_OF_MONTH, -1);
		}
	}

	/*******************************************************************************************************************
	 * Обновляет компоненты значениями из календаря. При этом не произходит ActionEvent
	 ******************************************************************************************************************/
	protected void updateComponents() {
		int day = -1;
		int month = -1;
		int year = -1;
		try {
			day = this.calendar.get(Calendar.DAY_OF_MONTH);
			month = this.calendar.get(Calendar.MONTH);
			year = this.calendar.get(Calendar.YEAR);
		} catch (Exception e) {
			// NOP
		}
		this.dayCombo.removeActionListener(this);
		this.monthCombo.removeActionListener(this);
		this.yearCombo.removeActionListener(this);
		if (day == -1) this.dayCombo.setSelectedItem(nullItem);
		else this.dayCombo.setSelectedItem(Integer.valueOf(day));
		if (month == -1) this.monthCombo.setSelectedItem(nullItem);
		else this.monthCombo.setSelectedItem(monthList[month]);
		if (year == -1) this.yearCombo.setSelectedItem(nullItem);
		else this.yearCombo.setSelectedItem(Integer.valueOf(year));
		this.dayCombo.addActionListener(this);
		this.monthCombo.addActionListener(this);
		this.yearCombo.addActionListener(this);
	}

	/*******************************************************************************************************************
	 * Обработчик событий элементов
	 ******************************************************************************************************************/
	@Override
    public void actionPerformed(ActionEvent e) {
		Object src = e.getSource();
		if (src == this.dayCombo || src == this.monthCombo || src == this.yearCombo) {
			updateCalendar();
			if (isDateComplete()) fireActionEvent("valueChanged");
		}
	}

	/*******************************************************************************************************************
	 * Оповещает слушателей о событии
	 ******************************************************************************************************************/
	protected void fireActionEvent(String command) {
		EventListener[] list = this.listenerList.getListeners(ActionListener.class);
		for (int i = 0; i < list.length; i++) {
			ActionListener l = (ActionListener) list[i];
			l.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, command));
		}
	}

	/*******************************************************************************************************************
	 * Добавляет слушателя компонента
	 ******************************************************************************************************************/
	public void addActionListener(ActionListener l) {
		this.listenerList.add(ActionListener.class, l);
	}

	/*******************************************************************************************************************
	 * Удаляет слушателя из списка слушателей
	 ******************************************************************************************************************/
	public void removeActionListener(ActionListener l) {
		this.listenerList.remove(ActionListener.class, l);
	}

	/*******************************************************************************************************************
	 * Устанавливает дату
	 ******************************************************************************************************************/
	public void setDate(long time) {
		this.calendar.setTime(new Date(time));
		updateComponents();
	}

	/*******************************************************************************************************************
	 * Возвращает текущую дату
	 ******************************************************************************************************************/
	public long getDate() {
		return (isDateComplete()) ? this.calendar.getTime().getTime() : 0;
	}

	/*******************************************************************************************************************
	 * Возвращает строковое представление выбранной даты
	 ******************************************************************************************************************/
	@Override
	public String toString() {
		if (isDateComplete()) {
			int day = this.calendar.get(Calendar.DAY_OF_MONTH);
			int month = this.calendar.get(Calendar.MONTH) + 1;
			int year = this.calendar.get(Calendar.YEAR);
			StringBuffer s = new StringBuffer();
			if (day < 10) s.append('0');
			s.append(day + ".");
			if (month < 10) s.append('0');
			s.append(month + ".");
			s.append(year);
			return s.toString();
		}
		return "<date incompleted>";
	}

	/*******************************************************************************************************************
	 * Установка шрифта
	 ******************************************************************************************************************/
	@Override
	public void setFont(Font font) {
		super.setFont(font);
		if (this.dayCombo != null) this.dayCombo.setFont(font);
		if (this.monthCombo != null) this.monthCombo.setFont(font);
		if (this.yearCombo != null) this.yearCombo.setFont(font);
	}
}
